package Containers;

import java.util.Iterator;

import CommonInterfaces.*;

 public class ListedMatrix<E extends IsCloneable> implements Matrix<E>
{
	public class ListedMatrixIter extends MatrixIter<E>
	{
		public boolean equals(ListedMatrixIter compare)
		{
			return _currentCell == compare._currentCell;
		}
		
		public ListedMatrixCell _currentCell;

		public ListedMatrixIter MoveLeft() {_currentCell = _currentCell._left; return this;}
		public ListedMatrixIter MoveRight() {_currentCell = _currentCell._right; return this;}
		public ListedMatrixIter MoveDown() {_currentCell = _currentCell._down; return this;}
		public ListedMatrixIter MoveUp() {_currentCell = _currentCell._up; return this;}
		
		public ListedMatrixIter MoveLeft (boolean isVertical)
		{
			if (isVertical)
				return MoveLeft();
			else
				return MoveUp();
		}
		
		public ListedMatrixIter MoveRight (boolean isVertical)
		{
			if (isVertical)
				return MoveRight();
			else
				return MoveDown();
		}
		

		public ListedMatrixIter MoveUp (boolean isVertical)
		{
			if (isVertical)
				return MoveUp();
			else
				return MoveLeft();
		}

		public ListedMatrixIter MoveDown (boolean isVertical)
		{
			if (isVertical)
				return MoveDown();
			else
				return MoveRight();
		}
		public E GetValue()
		{
			return _currentCell.value;
		}
		
		public void SetValue(E value)
		{
			_currentCell.value = value;
		}
		
		public ListedMatrixIter(ListedMatrixCell cell)
		{
			_currentCell = cell;
		}
		
		public ListedMatrixIter clone()
		{
			return new ListedMatrixIter(_currentCell);
		}
	}
	
	public class ListedMatrixCell implements IsCloneable
	{
		 
		private ListedMatrixCell _down, _up, _left, _right;
		public E value;
		
		public ListedMatrixCell () {}
		
		private ListedMatrixCell (ListedMatrixCell down, ListedMatrixCell up, ListedMatrixCell left, ListedMatrixCell right)
		{
			_down = down;
			_up = up;
			_left = left;
			_right = right;
		}
		
		public ListedMatrixCell clone()
		{
			ListedMatrixCell ret = new ListedMatrixCell(_down, _up, _left, _right);
			ret.value = (E)value.clone();
			
			return ret;
		}
	}
	
	private int _numOfRows, _numOfCols;
	private ListedMatrixCell _firstCell;
	
	public int numOfRows() {return _numOfRows;}
	public int numOfCols() {return _numOfCols;}
	
	public ListedMatrix<E>.ListedMatrixIter End()
	{
		return new ListedMatrixIter(null);
	}
	
	public ListedMatrix<E>.ListedMatrixIter Begin()
	{
		return new ListedMatrixIter(_firstCell);
	}
	
	public ListedMatrix(int cols, int rows)
	{
		if (cols == 0 || rows == 0)
			throw new IndexOutOfBoundsException("Can't intelize an empty listed matrix. (Sorry)");
		
		_numOfCols = cols;
		_numOfRows = rows;
				
		ListedMatrixCell tempCell = new ListedMatrixCell();

		_firstCell = tempCell;
		
		ListedMatrixCell rowIter1 = tempCell;
		ListedMatrixCell rowIter2 = null;
		ListedMatrixCell columnIter = tempCell;
		
		for (int y = 0; y < _numOfRows; y++)
		{
			for (int x = 1; x < _numOfCols; x++)
			{
				tempCell = new ListedMatrixCell();
				
				tempCell._left = rowIter1;
				tempCell._up = rowIter2;
				
				rowIter1._right = tempCell;
				
				if (rowIter2 != null)
				{
					rowIter2._down = tempCell;
					rowIter2 = rowIter2._right;
				}
				
				rowIter1 = tempCell;
			}
				
			if (y != _numOfRows - 1)
			{
				tempCell = new ListedMatrixCell();
				
				tempCell._up = columnIter;
				columnIter._down = tempCell;
				
				rowIter1 = tempCell;
				rowIter2 = columnIter._right;
				columnIter = tempCell;
			}
		}
	}
	
	public void AddSeam (Seam<E> seam, boolean isVertical)
	{
		if (seam.Elems.peek().clone().MoveLeft(isVertical).equals(End()))
			_firstCell = seam.Elems.peek()._currentCell;
		
		for (ListedMatrixIter iter: seam.Elems)
		{
			if (isVertical)
			{
				if (iter._currentCell._up != null)
					iter._currentCell._up._down = iter._currentCell;
				if(iter._currentCell._down != null)
					iter._currentCell._down._up = iter._currentCell;
				if(iter._currentCell._left != null)
					iter._currentCell._left._right = iter._currentCell;
				if(iter._currentCell._right != null)
					iter._currentCell._right._left = iter._currentCell;
			}
			else
			{
				if (iter._currentCell._left != null)
					iter._currentCell._left._right = iter._currentCell;
				if(iter._currentCell._right != null)
					iter._currentCell._right._left = iter._currentCell;
				if(iter._currentCell._up != null)
					iter._currentCell._up._down = iter._currentCell;
				if(iter._currentCell._down != null)
					iter._currentCell._down._up = iter._currentCell;
			}
		}
		
		if (isVertical)
			_numOfCols++;
		else
			_numOfRows++;
	}
	
	public Seam<E> DuplicateSeam (Seam<E> seam, boolean isVertical)
	{
		Seam<E> ret = new Seam<E>();
		
		ListedMatrixIter theOneBefore = null;
		ListedMatrixCell lastAddedCell = null;
		ListedMatrixCell newCell = null;
		
		for (ListedMatrixIter iter : seam.Elems)
		{
			newCell = iter._currentCell.clone();
			
			if (isVertical)
			{
				newCell._left = iter._currentCell;
				newCell._right = iter._currentCell._right;
			}
			else
			{
				newCell._up = iter._currentCell;
				newCell._down = iter._currentCell._down;				
			}
			
			if (!iter.clone().MoveUp(isVertical).equals(End()))
			{
				if (theOneBefore.clone().MoveLeft(isVertical).equals(iter.clone().MoveUp(isVertical)))
				{
					if (isVertical)
					{
						newCell._up = theOneBefore._currentCell;
						lastAddedCell._down = newCell._right;
					}
					else
					{
						newCell._left = theOneBefore._currentCell;
						lastAddedCell._right = newCell._down;
					}
				}
				else if (theOneBefore.clone().MoveRight(isVertical).equals(iter.clone().MoveUp(isVertical)))
				{
					if (isVertical)
					{
						newCell._up = lastAddedCell._right;
						lastAddedCell._down = iter._currentCell;
					}
					else
					{
						newCell._left = lastAddedCell._down;
						lastAddedCell._right = iter._currentCell;
					}
				}
				else
				{
					if (isVertical)
					{
						newCell._up = lastAddedCell;
						lastAddedCell._down = newCell;
					}
					else
					{
						newCell._left = lastAddedCell;
						lastAddedCell._right = newCell;
					}
				}
			}
			
			lastAddedCell = newCell;
			theOneBefore = iter.clone();
			ret.Elems.addLast(new ListedMatrixIter(newCell));
		}
		
		return ret;
	}
	
	public void RemoveSeam (Seam<E> seam, boolean isVertical)
	{
		if (isVertical)
			_numOfCols--;
		else
			_numOfRows--;
		
		//first cell check
		if (seam.Elems.peek()._currentCell._left == null && seam.Elems.peek()._currentCell._up == null)
			_firstCell = (isVertical ? seam.Elems.peek()._currentCell._right : seam.Elems.peek()._currentCell._down);
		
		//Left - Right correction
		for (ListedMatrixIter elem : seam.Elems)
		{
			if (isVertical)
			{
				if (elem._currentCell._left != null)
					elem._currentCell._left._right = elem._currentCell._right;
				if (elem._currentCell._right != null)
					elem._currentCell._right._left = elem._currentCell._left;
			}
			else
			{
				if (elem._currentCell._up != null)
					elem._currentCell._up._down = elem._currentCell._down;
				if (elem._currentCell._down != null)
					elem._currentCell._down._up = elem._currentCell._up;
			}
		}

		//Up - down Correction
		ListedMatrixIter iter = null, iter2;
		
		Iterator<ListedMatrix<E>.ListedMatrixIter> listIter = seam.Elems.listIterator(), listIter2 = seam.Elems.listIterator();
		
		if (!listIter.hasNext())
			return;
		
		listIter2.next();
		
		if (isVertical)
		{
			while(listIter2.hasNext())
			{
				iter = listIter.next();
				iter2 = listIter2.next();

				if (iter._currentCell._down != iter2._currentCell)
				{
					iter._currentCell._down._up = iter2._currentCell._up;
					iter2._currentCell._up._down = iter._currentCell._down;
				}
			}
		}
		else
		{
			while(listIter2.hasNext())
			{
				iter = listIter.next();
				iter2 = listIter2.next();

				if (iter._currentCell._right != iter2._currentCell)
				{
					iter._currentCell._right._left = iter2._currentCell._left;
					iter2._currentCell._left._right = iter._currentCell._right;
				}
			}
		}
	}
}
